package cmd

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"strings"

	"gitee.com/qianyucc/secure-tool/util"
	"github.com/spf13/cobra"
)

// hexCmd 16进制编码
var hexCmd = &cobra.Command{
	Use:   "hex",
	Short: "Use hex to encode and decode strings or files",
	Long: `Use hex to encode and decode strings or files.
	If you don't enter any text, it will read text from your system clipboard.`,
	Run: func(cmd *cobra.Command, args []string) {
		if err := cmd.ParseFlags(args); err != nil {
			fmt.Println(err)
			return
		}
		if IsDecode {
			if err := DecodeFileOrStringWithHex(args); err != nil {
				fmt.Println(err)
			}
		} else {
			if err := EncodeFileOrStringWithHex(args); err != nil {
				fmt.Println(err)
			}
		}
	},
}

// DecodeFileOrStringWithHex 使用Hex编码文件或字符串
func DecodeFileOrStringWithHex(args []string) (err error) {
	// 解码
	if InputFile == "" && OutputFile == "" {
		// 解码文字
		var text string
		if text = util.ReadTextFromClipboard(); len(args) == 0 && text == "" {
			return fmt.Errorf("invalid parameters, please enter the text you want to decode")
		}
		if len(args) > 0 {
			text = args[0]
		}
		var raw []byte
		if raw, err = hex.DecodeString(text); err != nil {
			return err
		}
		if IsUpperCase {
			raw = bytes.ToUpper(raw)
		}
		fmt.Println(string(raw))
		return
	}
	if InputFile == "" {
		return fmt.Errorf("invalid parameters, please enter the file name you want to decode")
	}
	OutputFile = util.GetOrDefaultString(OutputFile, fmt.Sprintf("%s-hex-dec.txt", InputFile))
	return DecodeFileWithHex(InputFile, OutputFile)
}

// DecodeFileWithHex 解码文件
func DecodeFileWithHex(inputFilePath, outputFilePath string) (err error) {
	var raw []byte
	if raw, err = ioutil.ReadFile(inputFilePath); err != nil {
		return err
	}
	// 避免某些编辑器乱加'\n'
	raw = bytes.Trim(raw, "\n")
	if raw, err = hex.DecodeString(string(raw)); err != nil {
		return err
	}
	if IsUpperCase {
		raw = bytes.ToUpper(raw)
	}
	if err = ioutil.WriteFile(outputFilePath, raw, 0644); err != nil {
		return err
	}
	return nil
}

// EncodeFileOrStringWithHex 使用Hex编码文件或字符串
func EncodeFileOrStringWithHex(args []string) (err error) {
	if InputFile == "" {
		// 处理文字
		var text string
		if text = util.ReadTextFromClipboard(); text == "" && len(args) == 0 {
			return fmt.Errorf("invalid parameters, please enter the text you want to encode")
		}
		if len(args) > 0 {
			text = args[0]
		}
		r := hex.EncodeToString([]byte(text))
		if IsUpperCase {
			r = strings.ToUpper(r)
		}
		util.PrintAndCopyTextToClipboardIgnoreError(r)
		return nil
	}
	// 处理文件
	if err = CheckFileSize(InputFile); err != nil {
		return err
	}
	// 如果没有输出文件名，就使用默认文件名
	OutputFile = util.GetOrDefaultString(OutputFile, fmt.Sprintf("%s-hex-enc.txt", InputFile))
	return EncodeFileWithHex(InputFile, OutputFile)
}

// EncodeFileWithHex 解码文件
func EncodeFileWithHex(inputFilePath, outputFilePath string) (err error) {
	var raw []byte
	if raw, err = ioutil.ReadFile(inputFilePath); err != nil {
		return err
	}
	hexStr := hex.EncodeToString(raw)
	if err := ioutil.WriteFile(outputFilePath, []byte(hexStr), 0644); err != nil {
		return err
	}
	return nil
}

func init() {
	rootCmd.AddCommand(hexCmd)

	hexCmd.Flags().StringVarP(&InputFile, "file", "f", "", "The file name you want to encode or decode")
	hexCmd.Flags().StringVarP(&OutputFile, "out", "o", "", `The file name after encoding or decoding. 
	When encoding, if you do not enter this flag, the output file will be named as ${fileName}-hex-enc.txt by default,
	When decoding, if you do not enter this flag, the output file will be named as ${fileName}-hex-dec.txt by default`)
	hexCmd.Flags().BoolVarP(&IsDecode, "decode", "d", false, "Indicates whether it is a decoding operation")
	hexCmd.Flags().BoolVarP(&IsUpperCase, "upperCase", "u", false, "Indicates whether the result is upper case")
}
